package com.allenja.eleviewer;

import com.threed.jpct.Logger;

import android.content.Context;
import android.graphics.PixelFormat;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

public class MyGLSurfaceView extends GLSurfaceView implements LocationListener {
	// Create the renderer
	private MyRenderer renderer;
	// CONSTANTS
	private static final int FIFTEEN_SECONDS = 1000 * 15 * 1;
	// Location control
	private LocationManager locationManager;
	private Location currentLocation;
	private Location lastKnownLocation;

	public MyGLSurfaceView(Context context) {
		super(context);

		// Set up OpenGL environment
		this.setEGLContextClientVersion(2);
		this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
		this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
		this.setZOrderOnTop(true);
		renderer = new MyRenderer(context);
		this.setRenderer(renderer);
		this.setPreserveEGLContextOnPause(true);
		
		// Get the location service
		locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
	}
	
	public void onStop() {
		// stop listening for location changes
		locationManager.removeUpdates(this);
		
		renderer.stop();
	}
	
	public void onPause() {
		// stop listening for location changes
		locationManager.removeUpdates(this);
	}
	
	public void onResume() {
		// resume listening for location changes
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, FIFTEEN_SECONDS, 0, this);
	}
	
	public boolean onTouchEvent(final MotionEvent event) {
		// handle the touch events
		queueEvent(new Runnable() {

			public void run() {
				// handle touch via renderer
				renderer.handleGLSurfaceTouchEvent(event);
			}
			
		});
		return true;
	}
	
	public Location updateRendererLocationData() {
		// handle location changed
		queueEvent(new Runnable() {
			
			public void run() {
				// handle location changed via renderer
				renderer.handleLocationChanged(currentLocation);
			}
		});
		return currentLocation;
	}

	public void makeUseOfNewLocation(Location location) {
		// check if current location is better than last known location
		if (location != null) {
			currentLocation = location;			
			lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
			
			if (isBetterLocation(lastKnownLocation, location)) {
				currentLocation = lastKnownLocation;
			}
		}
		else {
			Logger.log("GPS location: null");
			currentLocation = null;
		}
	}
	
	protected boolean isBetterLocation(Location location, Location currentBestLocation) {
		// validate if current location is better than last known location
	    if (currentBestLocation == null) {
	        return true;
	    }

	    // compare time stamps of locations
	    long timeDelta = location.getTime() - currentBestLocation.getTime();
	    boolean isSignificantlyNewer = timeDelta > FIFTEEN_SECONDS;
	    boolean isSignificantlyOlder = timeDelta < -FIFTEEN_SECONDS;
	    boolean isNewer = timeDelta > 0;

	    if (isSignificantlyNewer) {
	        return true;
	        
	    } else if (isSignificantlyOlder) {
	        return false;
	    }

	    // compare accuracy of locations
	    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
	    boolean isLessAccurate = accuracyDelta > 0;
	    boolean isMoreAccurate = accuracyDelta < 0;
	    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

	    // check if location is from same provider
	    boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());

	    if (isMoreAccurate) {
	        return true;
	        
	    } else if (isNewer && !isLessAccurate) {
	        return true;
	        
	    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
	        return true;
	        
	    }
	    return false;
	}

	private boolean isSameProvider(String provider1, String provider2) {
		// check if location is from same provider 
	    if (provider1 == null) {
	      return provider2 == null;
	    }
	    return provider1.equals(provider2);
	}	

	public void onLocationChanged(Location location) {
		// handle location changed
		makeUseOfNewLocation(location);
		updateRendererLocationData();
	}	

	public void onProviderDisabled(String provider) {
		Logger.log("Location provider disabled");
	}	

	public void onProviderEnabled(String provider) {
		Logger.log("Location provider enabled");		
	}	

	public void onStatusChanged(String provider, int status, Bundle extras) {
		// handle status changed
		switch (status) {
		
		case LocationProvider.OUT_OF_SERVICE:
			Logger.log("Status Changed: Out of Service");
			break;
			
		case LocationProvider.TEMPORARILY_UNAVAILABLE:
			Logger.log("Status Changed: Temporarily Unavailable");
			break;
			
		case LocationProvider.AVAILABLE:			
			Logger.log("Status Changed: Available");
			break;				
		}		
	}
}
